local skynet                            = require "skynet"
local utils                             = require "utils"
local proxypack                         = require "proxypack"
local msgdef                            = require "msgdef"
local pb                                = require "protobuf"
local util                              = require "util"
local table_def                         = require "table_def"
local sharedata                         = require "skynet.sharedata"
local error_code                        = require "error_code"
local reason                            = require "reason"
local kpqz                              = require "niuniu.kpqz"
local cocall                            = require "cocall"
local constant                          = require "constant"
local table_insert                      = table.insert
local table_remove                      = table.remove
local table_sort                        = table.sort
local table_unpack                      = table.unpack
local math_floor                        = math.floor
local math_random                       = math.random
local math_abs                          = math.abs
local string_format                     = string.format
local string_gmatch                     = string.gmatch


local global_configs
local player_info_list                  = {}
local curr_locked_uids                  = {}
local uid_game_session_map              = {}  --标记房间所有玩家的clientId 包括观战者的clientId
local seated_position_uid_map           = {}  --坐下的玩家的顺序
local noseat_players_list               = {}  --无座玩家的映射
local had_paid_fee_uid_map              = {}
local pass_authorized_uid_map           = {}
local uid_authorized_time_map           = {}
local two_round_action_record_map       = {}
local all_player_record_map             = {}

local card_type_times_map               = {}
local sorted_can_bet_times_list         = {}
local sorted_can_push_times_list        = {}

local game_bet_result_list              = {}
local curr_kpqz_instance
local curr_round
local curr_status
local self_table_type, self_table_id, self_table_gid, self_game_config

local handler                           = {}
local internal                          = {}

local PALYER_STATUS_SITDOWN             = 1
local PALYER_STATUS_READY               = 2
local PALYER_STATUS_WATCH               = 3
local PLAYER_STATUS_PLAYING             = 4

local STANDUP_NOT_ENOUGH_COINS          = 1
local STANDUP_NOT_OPERATE               = 2

local recycling_table_start_time
local RECYCLEING_TABLE_TIMEOUT          = 60
local MAX_WATCH_PLAYER_NUM              = 100
local MIN_PLAYER_NUMBER                 = 2
local NO_OPERATE_ROUND_LIMIT            = 2
local COCALL_TIMEOUT                    = 5

local DISS_REASON_CREATOR               = 1
local DISS_REASON_UNSTART_TIME_OUT      = 2
local DISS_REASON_TIME_OUT              = 3
local DISS_REASON_ROUND_OVER            = 4
local DISS_REASON_CLOSE_SERVER          = 5


local OFFLINE_CLIENT_FD                 = -1
local ROBOT_CLIENT_FD                   = -2
local REGISTER_CLIENT_FD                = 0

local room_owner_confirm_status         = false
local is_round_over                     = false
local closing_server                    = false
local status_change_flag                = false
local TABLE_STATUS_STRING               = {}


local GAME_STATUS_INIT                  = 0
local GAME_STATUS_WAIT_START            = 1
local GAME_STATUS_START                 = 2
local GAME_STATUS_BET                   = 3
local GAME_STATUS_CACL_CATTLE           = 4
local GAME_STATUS_SHOW_AND_SETTLE       = 5
local GAME_STATUS_ROOM_OWNER_CONFIRM    = 6

local GAME_TIME_WAIT_START              = 2
local GAME_TIME_START                   = 6
local GAME_TIME_BET                     = 7
local GAME_TIME_CACL_CATTLE             = 9
local GAME_TIME_SHOW_AND_SETTLE         = 10


-- local GAME_TIME_WAIT_START           = 1
-- local GAME_TIME_START                = 1
-- local GAME_TIME_BET                  = 1
-- local GAME_TIME_CACL_CATTLE          = 1
-- local GAME_TIME_SHOW_AND_SETTLE      = 1


local game_start_wait_start_time
local game_start_start_time
local game_start_bet_time
local game_start_calc_cattle_time
local game_start_show_and_settle_time



return function(params)
local tablesvr_id = tonumber(skynet.getenv "server_id")

local function set_table_status_string()
    TABLE_STATUS_STRING[GAME_STATUS_INIT]               = "init"
    TABLE_STATUS_STRING[GAME_STATUS_ROOM_OWNER_CONFIRM] = "room owner confirm"
    TABLE_STATUS_STRING[GAME_STATUS_WAIT_START]         = "wait start"
    TABLE_STATUS_STRING[GAME_STATUS_START]              = "start"
    TABLE_STATUS_STRING[GAME_STATUS_BET]                = "bet"
    TABLE_STATUS_STRING[GAME_STATUS_CACL_CATTLE]        = "calc cattle"
    TABLE_STATUS_STRING[GAME_STATUS_SHOW_AND_SETTLE]    = "show card and settle"
end

local function send_to_gateway(uid, client_fd, ...)
    if client_fd <= 0 then
        return
    end

    return utils.send_to_gateway(0, uid, client_fd, ...)
end

local function notify_others(action, excepted_uid, msg)
    --先广播给桌子上的人
    for uid, fd in pairs(uid_game_session_map) do
        if uid ~= excepted_uid then
            send_to_gateway(uid, fd, action, msg)
        end
    end
end

local function notify_all(action, msg)
    return notify_others(action, nil, msg)
end

function return_comma_split(str, split_char)
    local sub_str_tab = {}

    for mu_id in string_gmatch(str, "(%d+)|*") do
        table_insert(sub_str_tab, mu_id)
    end

    table_sort(sub_str_tab)

    return sub_str_tab
end

local function get_extra_bill_info(uid)
    local t = { game_type = self_game_type, table_type = self_table_type }
    local player_info = player_info_list[uid]

    if not player_info then
        return t
    end

    t.client_version = player_info.client_version
    t.channel = player_info.channel
    t.device_id = player_info.device_id
    t.device_brand = player_info.device_brand
    t.os_system = player_info.os_system
    t.platform = player_info.platform
        
    return t
end

local function get_seated_num()
    local num = 0

    for position, uid in pairs(seated_position_uid_map) do
        if uid then
            num = num + 1
        end
    end

    return num
end

local function get_no_seated_num()
    return #noseat_players_list
end

local function get_all_player_num()
    local num = get_seated_num()
    return num + #noseat_players_list
end

local function watch_session(game_session, uid, observing)
    if game_session <= 0 then
        return
    end

    local gateway_id = game_session >> 31
    local request

    if observing then
        request = "observe_fd"
    else
        request = "unobserve_fd"
    end

    R().gateway(gateway_id):send(".watchdog", "tablesvr", request, R().get_source(), game_session, uid)
end

local function game_session_correct(uid, game_session)
    if uid_game_session_map[uid] ~= game_session then
        errlog(string_format("uid_game_session_map[%d](%d) ~= game_session(%d)", uid or 0, uid_game_session_map[uid] or 0, game_session or 0))
        return false
    end

    return true
end

local function game_status_correct(correct_game_status)
    if curr_status ~= correct_game_status then
        errlog(string_format("curr_status(%d)(%s) ~= correct_game_status(%d)(%s)", curr_status, 
            TABLE_STATUS_STRING[curr_status], correct_game_status, TABLE_STATUS_STRING[correct_game_status]))
        return false
    end

    return true
end

local function check_player_info_exist(uid)
    local player_info = player_info_list[uid]
    if not player_info then
        errlog(string_format("uid(%d) no player info", uid))
        return false, error_code.NO_SUCH_PLAYER
    end

    return true
end

local function set_player_status(uid, status)
    local player_info = player_info_list[uid]
    player_info.status = status
end

local function add_to_noseat(uid)
    table_insert(noseat_players_list, uid)
    set_player_status(uid, PALYER_STATUS_WATCH)
    return true
end

local function system_do_banker(uid)
    return uid == constant.SYSTEM_DO_BANKER_UID
end

local function remove_from_noseat(uid)
    local noseat_idx

    for i, suid in ipairs(noseat_players_list) do
        if suid == uid then
            noseat_idx = i
            break
        end
    end

    if noseat_idx then
        return uid == table_remove(noseat_players_list, noseat_idx)
    else
        return false
    end
end

local function get_rsp_player_info(player_info)
    return { 
        uid = player_info.uid, 
        name = player_info.name, 
        coins = player_info.total_score, 
        icon = player_info.icon, 
        position = player_info.position, 
        sex = player_info.sex,
        status = player_info.status,
     }
end

local function update_player_status(uid, status)
    local player_info = player_info_list[uid]
    player_info.status = status
end

local function deal_sit_down(uid, position)
    local player_info = player_info_list[uid]
    
    seated_position_uid_map[position] = uid
    player_info.position = position
    player_info.status = PALYER_STATUS_SITDOWN
    remove_from_noseat(uid)
    dbglog("-----------call----remove_from_noseat-------------------")

    notify_all("kpqz.NTF_SITDOWN", { player = get_rsp_player_info(player_info) })
end

local function get_next_can_sit_position()
    local can_position = 0

    for position, uid in pairs(seated_position_uid_map) do
        if not uid then
            can_position = position
            break
        end
    end

    return can_position
end

local function deal_auto_sit_down(player_info)
    local uid = player_info.uid
    local own_coins = player_info.coins
    local can_position = get_next_can_sit_position()

    if can_position == 0 then
        dbglog(string_format("uid(%d) can not sitdown", uid))
        return false, error_code.CANT_SITDOWN
    end

    deal_sit_down(uid, can_position)
    return true
end

local function clear_player_action_record(uid)
    for _, action_record_map in pairs(two_round_action_record_map) do
        action_record_map[uid] = nil
    end

    dbglog(string_format("uid(%d) clear operate record ok", uid))
    return
end

local function deal_stand_up(uid, op_code)
    local player_info = player_info_list[uid]

    seated_position_uid_map[player_info.position] = false
    player_info.position = nil   
    add_to_noseat(uid)
    dbglog("-----------call----add_to_noseat-------------------")
    clear_player_action_record(uid)   

    notify_all("kpqz.NTF_STANDUP", { uid = uid, reason = op_code })
end


local function lock_one_player(uid)
    local ok, succ = R().exdbsvr(1):call(".tlock_mgr", "set_on_table", uid, self_table_gid, self_table_gid)
    if not ok then
        errlog(string_format("call exdbsvr set_on_table not ok error", uid, self_table_id))
        return false
    end

    if not succ then
        errlog(string_format("call exdbsvr set_on_table not succ error", uid, self_table_id))
        return false
    end

    curr_locked_uids[uid] = true
    return true
end

local function unlock_one_player(uid)
    R().exdbsvr(1):send(".tlock_mgr", "unset_on_table", uid, self_table_gid)    
    R().friendsvr(1):send(".info_mgr","update_info", uid, { curr_game_type = 0 })
    curr_locked_uids[uid] = nil
end

local function clear_player_info(uid)
    uid_game_session_map[uid] = nil
    player_info_list[uid] = nil
    remove_from_noseat(uid)
    clear_player_action_record(uid)
end

local function clear_and_unlock_offline_player(uid)
    dbglog(string_format("uid(%d) clear offlined player", uid))
    unlock_one_player(uid)

    local player_info = assert(player_info_list[uid])

    if player_info.position then
        deal_stand_up(uid, 0)
    end

    --clear_player_info(uid)
end

local function clear_offline_players()
    for uid, status in pairs(uid_game_session_map) do
        if status == OFFLINE_CLIENT_FD then
            local ok, ret = xpcall(clear_and_unlock_offline_player, debug.traceback, uid)
            if not ok then
                errlog(uid, ret)
            end
        end
    end
end

local function get_player_enter_data(uid)
    local ok, base_data = R().basesvr{ key = uid }:call(".msg_handler", "get_base_data", uid)
    if not ok then
        errlog(string_format("uid(%d) call basesvr get_base_data error", uid))
        return
    end

    local ok, enter_data = R().hallsvr{ key = uid }:call(".msg_handler", "get_enter_data", uid)
    if not ok then
        errlog(string_format("uid(%d) call hallsvr get_enter_data error", uid))
        enter_data = {}
    end

    dbglog(string_format("base_data(%s) enter_data(%s)", tostring_r(base_data), tostring_r(enter_data)))

    local player_info = { 
        uid = base_data.uid, 
        name = enter_data.name or "", 
        coins = base_data.coins, 
        score = 0,
        icon = enter_data.icon or "", 
        sex = enter_data.sex, 
        roomcards = base_data.roomcards,
        safe_box = base_data.safe_box,
     }

    return player_info
end

local function get_rsp_seat_player_info_list()
    local rsp_list = {}

    for position, uid in pairs(seated_position_uid_map) do
        if uid then
            local player_info = player_info_list[uid]
            local rsp_player_info = get_rsp_player_info(player_info)
            table_insert(rsp_list, rsp_player_info)
        end
    end

    return rsp_list
end

local function get_rsp_player_info_list()
    local rsp_list = {}

    for uid, player_info in pairs(player_info_list) do
        local rsp_player_info = get_rsp_player_info(player_info)
        table_insert(rsp_list, rsp_player_info)
    end

    return rsp_list
end

local function get_seated_list()
    local seated_uid_list = {}

    for position, uid in pairs(seated_position_uid_map) do
        if uid then
            local play_info = player_info_list[uid]
            table_insert(seated_uid_list, play_info)
        end
    end

    return seated_uid_list
end

local function get_table_info()
     local table_info = {}

     table_info.dizhu = self_game_config.dizhu
     table_info.cost = self_game_config.cost
     table_info.max_seat_num = self_game_config.max_seat_num
     table_info.dizhu = self_game_config.dizhu
     table_info.table_name = self_game_config.name or ""

     table_info.created_time = self_game_config.created_time
     table_info.creator_uid = self_game_config.creator_uid
     table_info.total_round = self_game_config.total_round
     table_info.password = self_game_config.password
     table_info.is_authorized_seat = self_game_config.is_authorized_seat
     table_info.is_aa = self_game_config.is_aa
     table_info.is_creator_start = self_game_config.is_creator_start
     table_info.least_num = self_game_config.least_num
     table_info.times_option = self_game_config.times_option
     table_info.special_type = self_game_config.special_type
     table_info.push_times = self_game_config.push_times

     return table_info
end

local function get_enter_info(uid)
    local enter_info = {}
   
    enter_info.table_info = get_table_info()
    return enter_info
end

local function get_aa_cost()
    local friendroomdata = assert(global_configs.friendroomdata)
    local multiple = math_floor(self_game_config.total_round / 10)
    local cost = assert(friendroomdata[self_table_type].aa_cost * multiple)
    return cost
end

local function add_to_watch_logic(uid, enter_data)
    local watch_num = get_no_seated_num()

    if watch_num >= MAX_WATCH_PLAYER_NUM then
        dbglog(string_format("uid(%d) watch_num(%d) MAX_WATCH_PLAYER_NUM(%d)", uid, watch_num, MAX_WATCH_PLAYER_NUM))
        unlock_one_player(uid)
        return false
    end

    dbglog(string_format("uid(%d) add to watch list", uid))
    player_info_list[uid] = enter_data
    add_to_noseat(uid)
    return true
end

local function can_not_seat(uid, enter_data)
    local seated_num = get_seated_num()
    if seated_num >= self_game_config.max_seat_num then
        dbglog(string_format("uid(%d) seated_num(%d) max_seat_num(%d)", uid, seated_num, self_game_config.max_seat_num))
        return true
    end    

    local can_position = get_next_can_sit_position()
    if can_position == 0 then
        dbglog(string_format("uid(%d) no position", uid))
        return true
    end

    if self_game_config.is_aa and not had_paid_fee_uid_map[uid] then
        local cost_card = get_aa_cost()

        if uid ~= self_game_config.creator_uid then
            if enter_data.roomcards < cost_card then
                dbglog(string_format("uid(%d) roomcards(%d) < cost_card(%d)", uid, enter_data.roomcards, cost_card))
                return true
            end
        end
    end

    return false    
end

local function init_player_record(player_info)
    local player_record = all_player_record_map[uid]
    if not player_record then
        player_record = {}
        all_player_record_map[player_info.uid] = player_record

        player_record.uid = player_info.uid
        player_record.total_score = 0
        player_record.name = player_info.name
        player_record.win_times = 0
        player_record.play_times = 0
        player_record.icon = player_info.icon
        player_record.sex = player_info.sex
        player_record.do_banker_times = 0
    end
end

local function enter_seat_logic(uid, enter_data)
    if not lock_one_player(uid) then
        errlog(string_format("uid(%d) lock error", uid))
        return false, error_code.CANNOT_ENTER_FTABLE_LOCKED
    end
	local need_init = true;

    if not self_game_config.is_authorized_seat then
        if can_not_seat(uid, enter_data) then
            if not add_to_watch_logic(uid, enter_data) then
                return false, error_code.FULL_WATCH_PLAYERS_IN_FRIEND_ROOM
            end
            
            unlock_one_player(uid)
            return false, error_code.ROOMCARD_IS_NOT_ENOUGH
        end
        
		if player_info_list[uid] then
			need_init = false
		end

        player_info_list[uid] = enter_data    
        deal_sit_down(uid, get_next_can_sit_position())
        update_player_status(uid, PALYER_STATUS_READY)
    else
    
    	if player_info_list[uid] then
			need_init = false
		end
    
    
        player_info_list[uid] = enter_data
    end
    
	if need_init then
    	init_player_record(enter_data)
    end
    
    return true
end

local function multi_enter_logic(uid, enter_data)
    local player_info = player_info_list[uid]

    if player_info and player_info.position then
        player_info.name = enter_data.name
        player_info.icon = enter_data.icon
        player_info.coins = enter_data.coins
        player_info.sex = enter_data.sex
        player_info.roomcards = enter_data.roomcards
    end
end

local function deal_enter(uid, client_fd)    
    if is_round_over then
        errlog(string_format("uid(%d) round over", uid))
        return false, error_code.TABLE_IS_NOT_EXISTING
    end


    local enter_data = get_player_enter_data(uid)
    if not enter_data then
        errlog(string_format("uid(%d) get enter data error", uid))
        return false, error_code.CANNOT_ENTER_TEMOPORARILY
    end



    if uid_game_session_map[uid] then 
        multi_enter_logic(uid, enter_data)
        uid_game_session_map[uid] = client_fd
        return true
    end    

    local ok, ret = enter_seat_logic(uid, enter_data)
    if not ok then
        return false, ret
    end
    
    
    
    

    
    

    uid_game_session_map[uid] = client_fd
    return true
end

local function player_num_meet_condition()
    if get_seated_num() >= self_game_config.least_num then
        return true
    end
    
    return false
end

local function update_player_roomcards(uid, roomcards)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.roomcards = roomcards
    else
        errlog(string_format("uid(%d) roomcards(%d) player info not exist", uid, roomcards))
    end
end

local function update_player_coins(uid, coins)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.coins = coins
    else
        errlog(string_format("uid(%d) coins(%d) player info not exist", uid, coins))
    end
end

local function add_coins(uid, coins, reason, params)
    local ok, succ, ret = R().basesvr{ key = uid }:call(".msg_handler","add_coins", uid, coins, reason, params)
    if not ok then
        errlog(string_format("failed to add coins uid(%d) coins(%d)", uid, coins))
        return false
    end
    if not succ then
        errlog(string_format("failed to add coins uid(%d) coins(%d)", uid, coins))
        return false
    end

    update_player_coins(uid, ret.curr)
    local fd = assert(uid_game_session_map[uid])    
    utils.notify_money_changed(uid, { coins = ret.curr }, fd)
    return ret
end

local function reduce_coins(uid, money, reason, ...)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "reduce_coins", uid, money, reason, ...)
    if not ok then
        errlog(string_format("uid(%d) call basesvr reduce coins error", uid))
        return false
    end

    if not succ then
        errlog(string_format("uid(%d) basesvr reduce coins succ == false error", uid))
        return false
    end

    update_player_coins(uid, ret.curr)
    local fd = assert(uid_game_session_map[uid])
    utils.notify_money_changed(uid, { coins = ret.curr }, fd)
    return ret
end

local function write_card_record_log(bet_result_list, pay_fee_map)
    local winners = {}
    local losers = {}

    for uid, uid_bet_result in pairs(bet_result_list) do
        local player_info = player_info_list[uid]
        local left_coins = 0

        if player_info then
            left_coins = player_info.coins
        end

        if uid_bet_result.result_coins > 0 then
            table_insert(winners, { 
                uid = uid,  
                left_score = left_coins,  
                add_score = uid_bet_result.result_coins,  
                pay_fee = pay_fee_map[uid],  
                is_robot = system_do_banker(uid),  
            })
        else
            table_insert(losers, { 
                uid = uid,  
                left_score = left_coins,  
                add_score = uid_bet_result.result_coins,  
                pay_fee = pay_fee_map[uid],  
                is_robot = system_do_banker(uid),
            })
        end
    end

    local str_date = os.date("%Y%m%d%H%M%S")
    
    billlog({ op = "card_record",  
        table_gid = str_date .. "_" .. self_table_id,  
        game_type = self_game_type, 
        table_type = self_table_type,  
        begin_time = game_start_bet_time, 
        end_time = game_start_game_over_time,  
        curr_round = curr_round,  
        password = self_password, 
        winner_list = winners,  
        loser_list = losers, 
        r = reason.CARD_RECORD 
    })
end

local function notify_all_roomcard_change(results)
    for uid, ret in pairs(results) do
        local player_info = player_info_list[uid]
        local fd = uid_game_session_map[uid]
        utils.notify_money_changed(uid, { roomcards = ret.curr }, fd)
    end
end

local function settle_event_trigger(bet_result_map)
    for uid, uid_bet_result in pairs(bet_result_map) do
        if system_do_banker(uid) then
            goto continue
        end

        -- --触发赢钱事件
        -- if uid_bet_result.result_coins > 0 then
        --     R().exdbsvr(1):send(".event_mgr", "trigger", "on_winning", { 
        --         uid = uid, 
        --         game_type = self_game_type, 
        --         table_type = self_table_type, 
        --         coins = uid_bet_result.result_coins,
        --      })
        -- end

        -- R().exdbsvr(1):send(".event_mgr", "trigger", "on_play", { 
        --     uid = uid, 
        --     game_type = self_game_type, 
        --     table_type = self_table_type 
        -- })

        ::continue::
    end
end

local function update_player_result_data(uid, uid_bet_result)
    local player_record = all_player_record_map[uid]
    local player_info = player_info_list[uid]

    player_record.play_times = player_record.play_times + 1
    player_record.total_score = player_record.total_score + uid_bet_result.result_coins
    player_info.total_score = player_record.total_score

    if uid_bet_result.result_coins > 0 then
        player_record.win_times = player_record.win_times + 1
    end    

    if uid_bet_result.is_banker then
        player_record.do_banker_times = player_record.do_banker_times + 1
    end
end

local function send_gameover_msg(bet_result_map)
    local rsp_bet_result_list = {}

    for uid, uid_bet_result in pairs(bet_result_map) do
        table_insert(rsp_bet_result_list, uid_bet_result)
        update_player_result_data(uid, uid_bet_result)    
    end

     notify_all("kpqz.NTF_GAME_RESULT", { 
        bet_result_list = rsp_bet_result_list, 
        left_time = GAME_TIME_SHOW_AND_SETTLE,
        player_info_list = get_rsp_seat_player_info_list()
     })

    game_bet_result_list = rsp_bet_result_list
end

local function check_two_round_no_action(action_uid)
    dbglog(string_format("3----------------->two_round_action_record_map(%s) action_uid(%d)", tostring_r(two_round_action_record_map), action_uid))
    local round1_action_record_map = two_round_action_record_map[1]
    local round2_action_record_map = two_round_action_record_map[2]

    if not round2_action_record_map then
        return false
    end

    local round1_record = round1_action_record_map[action_uid]
    local round2_record = round2_action_record_map[action_uid]

    dbglog(string_format("4----------------round1_record(%s) round2_record(%s)", tostring_r(round1_record), tostring_r(round2_record)))

    if not round2_record then
        return false
    end

    
	if not round2_record[kpqz.BET_ACTION] and not round2_record[kpqz.CACL_CATTLE_ACTION] and not round2_record[kpqz.GRAB_BANKER_ACTION] and
		not round1_record[kpqz.BET_ACTION] and not round1_record[kpqz.CACL_CATTLE_ACTION] and not round1_record[kpqz.GRAB_BANKER_ACTION]
	then
		return true
	end

	--[[
    if not round2_record[kpqz.BET_ACTION] and not round2_record[kpqz.CACL_CATTLE_ACTION] and not round2_record[kpqz.GRAB_BANKER_ACTION] then
        return true
    end

    if not round1_record[kpqz.BET_ACTION] and not round1_record[kpqz.CACL_CATTLE_ACTION] and not round1_record[kpqz.GRAB_BANKER_ACTION] then
        return true
    end
    --]]
 

    return false
end

local function when_not_action_standup()
    for _, uid_bet_result in pairs(game_bet_result_list) do
        local uid = uid_bet_result.uid

        if check_two_round_no_action(uid) then
            deal_stand_up(uid, STANDUP_NOT_OPERATE)
        end
    end    
end

local function update_two_round_action_record_map(action_record_map)
    dbglog(string_format("1----------------->two_round_action_record_map(%s)", tostring_r(two_round_action_record_map)))
    if #two_round_action_record_map == 2 then
        table_remove(two_round_action_record_map)
    end

    table_insert(two_round_action_record_map, 1, action_record_map)
    dbglog(string_format("2----------------->two_round_action_record_map(%s)", tostring_r(two_round_action_record_map)))
end

local function get_player_simple_data(uid)
    local player_info = player_info_list[uid]
    local simple_data = {
                    name = player_info.name,
                    icon = player_info.icon,
                    sex = player_info.sex
    }
    --获取玩家当前的最新的总积分数
    return simple_data
end


local function get_result_info(game_result)
    local rand_info = {}
    local winners = game_result.winners
    local losers = game_result.losers
    --[[
    for _, round_info in ipairs(winners) do
        rand_info[round_info.uid] = round_info.add_score
    end
    for _, round_info in ipairs(losers) do
        rand_info[round_info.uid] = round_info.add_score
    end
    ]]--
    for uid, round_info in pairs(game_result.bet_result_map) do
    	rand_info[uid] = round_info.result_coins
    end
    return rand_info
end

local function audit_game_result(game_result)
    local bet_result_map = game_result.bet_result_map
    local game_valid = game_result.game_valid
    local pay_fee_map = game_result.pay_fee_map
    local action_record_map = game_result.action_record_map
    local round_info = get_result_info(game_result)

    dbglog(string_format("game_result(%s)", tostring_r(game_result)))

    if not game_valid then
        errlog("game_valid is false, no need to settle")
        send_gameover_msg(bet_result_map, nil)
        return false
    end
    
	local save_info = {}
    for _uid,_score in pairs(round_info) do
        local simple_data = get_player_simple_data(_uid)
        if simple_data then
            table_insert(save_info,{uid = _uid,name = simple_data.name,score =_score,icon = simple_data.icon})
        end
    end

   
    --记录写入数据库
    local conf = {
        table_type = self_table_type,
        round_info = save_info,
        curr_round = curr_round,
        count = self_game_config.total_round,
        --[[
        least_num = 1,
        times_option = 1,
        special_type = 1,
        push_times = 1,
        --]]
    }

	
    local ok,key = R().exdbsvr(1):call('.ftable_handler','save_round_records',self_game_config.password, conf)
    if not ok then
        errlog(uid,"save_record failed")
        --return false
    end
    

    send_gameover_msg(bet_result_map)    
    settle_event_trigger(bet_result_map)
    write_card_record_log(bet_result_map, pay_fee_map)
    update_two_round_action_record_map(action_record_map)
    return true
end

local function unlock_all_players()
    for uid, _ in pairs(uid_game_session_map) do
        unlock_one_player(uid)
    end
end

local function get_record_list()
    local record_list = {}
	if curr_round == 0 then
		return record_list
	end


    for uid, player_record in pairs(all_player_record_map) do
        table_insert(record_list, player_record)
    end

    return record_list
end

local function unwatch_uid_session()
    for uid, fd in pairs(uid_game_session_map) do
        if fd > 0 then
            watch_session(fd, uid, false)
        end
    end
end

local function dissmiss_table(dismiss_reason)
    unwatch_uid_session()
    unlock_all_players()

    local msg = {}
    msg.record_list = get_record_list()
    msg.table_info = get_table_info()
    msg.curr_round = curr_round
    msg.reason = dismiss_reason

    notify_all("kpqz.NTF_FTABLE_DISS", msg)    
    R().fmatchsvr(1):send(".table_mgr", "dismiss_friend_table", self_game_config.password, self_table_gid)
    billlog({ op = "close_table", status = curr_status, password = self_game_config.password })
    return true
end

local function check_empty_table_recycle(curr_time)
    --如果有开始过对局了，则不做空桌回收
    if curr_round > 0 then
        return false
    end

    --如果当前有人在这里，则不做空桌回收
    if next(uid_game_session_map) then
        if recycling_table_start_time then
            recycling_table_start_time = nil
        end
        return false
    end

    if not recycling_table_start_time then
        recycling_table_start_time = curr_time
        return false
    end

    --假如已经过了1分钟还没有人进来，则可以回收该桌子
    if curr_time - recycling_table_start_time >= RECYCLEING_TABLE_TIMEOUT then
        return true
    end

    return false
end

local function check_close_table(curr_time)


    local dismiss = false
    local dismiss_reason
    local recycle

    if not dismiss and is_round_over then
        dismiss = true
        dismiss_reason = DISS_REASON_ROUND_OVER
        recycle = true
    end

    if not dismiss and curr_round == 0 and curr_time >= self_game_config.unstart_expiry_time then
        dismiss = true
        dismiss_reason = DISS_REASON_UNSTART_TIME_OUT
        recycle = true
    end

    if not dismiss and curr_round > 0 and curr_time >= self_game_config.expiry_time then
        dismiss = true
        dismiss_reason = DISS_REASON_TIME_OUT
    end

    if not dismiss and curr_round ~= 0 and colsing_server == true then
        dismiss = true
        dismiss_reason = DISS_REASON_CLOSE_SERVER
    end

    if colsing_server == true then
        recycle = true
    end

    if not recycle and check_empty_table_recycle(curr_time) then
        recycle = true
    end


    if not dismiss and not recycle then
        --既不解散也不回收
        return false
    end

    if dismiss then
        --解散的话需要显示去请求，因为有退房卡补偿
        local ok,ret = xpcall(dissmiss_table, debug.traceback, dismiss_reason)
        if not ok then
            errlog(ret)
        end
    else
        --此处是回收，可以不通知fmatchsvr
        assert(recycle)
        --玩家解除桌子锁定
        unlock_all_players()
    end

    local uid_list = {}
    for uid, _ in pairs(player_info_list) do
        table_insert(uid_list, uid)
    end

    skynet.send(".table_mgr", "lua", "on_table_delete", self_table_id, uid_list)
    skynet.exit()
    return true
end

local function get_first_four_cards(side_cards_map, side)
    local tmp_cards_list = side_cards_map[side]
    local self_card_list = {}

    for i = 1, 4 do 
        table_insert(self_card_list, tmp_cards_list[i])
    end

    return self_card_list
end

local function get_grab_banker_max_times(uid)
    local max_times = 4
    return max_times
end

local function send_ntf_start_msg()
    local side_cards_map = curr_kpqz_instance:get_side_cards_map()
    local player_data_map = curr_kpqz_instance:get_player_data_map()
    local rsp_player_info_list = get_rsp_seat_player_info_list()    

    for uid, player_data in pairs(player_data_map) do
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_START
        msg.grab_banker_max_times = get_grab_banker_max_times(uid)
        msg.self_card_list = get_first_four_cards(side_cards_map, player_data.side)
        msg.player_info_list = rsp_player_info_list
        msg.curr_round = curr_round

        send_to_gateway(uid, game_session, "kpqz.NTF_START", msg)
    end

    dbglog(string_format("------------noseat_players_list(%s)", tostring_r(noseat_players_list)))

    for _, uid in pairs(noseat_players_list) do
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_START
        msg.grab_banker_max_times = 0
        msg.self_card_list = {}
        msg.player_info_list = rsp_player_info_list
        msg.curr_round = curr_round

        send_to_gateway(uid, game_session, "kpqz.NTF_START", msg)
    end
end

local function is_robot(uid)
    return uid_game_session_map[uid] == ROBOT_CLIENT_FD
end

local function return_one_roomcards(uid, roomcards)
    local ok, succ, ret = R().basesvr{ key = self_game_config.creator_uid }:call(".msg_handler", "add_roomcards",
        uid, roomcards, reason.FRIEND_ERROR_ROOMCARD_BACK)
    if not ok then
        errlog(string_format("uid(%d) call basesvr add_roomcards error", uid))
        return false
    end

    if not succ then
        errlog(string_format("uid(%d) call basesvr add_roomcards error", uid))
        return false
    end

    update_player_roomcards(uid, ret.curr)
    local fd = assert(uid_game_session_map[uid])    
    utils.notify_money_changed(uid, { roomcards = ret.curr }, fd)
    return ret
end

local function return_roomcards(src_results, cost_card)
    local return_op_deal = {}

    for uid, ret in pairs(src_results) do
        if ret then
            table_insert(return_op_deal, { f = return_one_roomcards, id = uid, params = { uid, cost_card } })  
        end
    end
    
    local ok, co_results = cocall(COCALL_TIMEOUT, return_op_deal)
    if not ok then
        errlog(string_format("failed to cocall co_results(%s)", tostring_r(co_results)))
    end 

    return co_results
end

local function get_pay_result_detail(co_results, pay_ok_map)
    for uid, ret in pairs(co_results) do
        if ret then
            pay_ok_map[uid] = ret
        end
    end

    if not player_num_meet_condition() then
        return false
    end

    return true
end

local function reduce_roomcards(uid, cost)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "reduce_roomcards", 
        uid, cost, reason.FRIEND_TABLE_ROOMCARD_COST, get_extra_bill_info(uid))
    if not ok then
        errlog(string_format("uid(%d) cost(%d) call basesvr reduce_roomcards", uid, cost))
        return false
    end

    --房卡不够
    if not succ then
        errlog(string_format("uid(%d) cost(%d) call basesvr reduce_roomcards", uid, cost))
        return false
    end

    update_player_roomcards(uid, ret.curr)
    utils.notify_money_changed(uid, { roomcards = ret.curr }, uid_game_session_map[uid])
    return ret
end

local function need_pay(uid)
    if uid and uid ~= self_game_config.creator_uid and not had_paid_fee_uid_map[uid] then
        return true
    end

    return false
end

local function pay_fee_success()
    if not self_game_config.is_aa then
        return true
    end

    local deduct_pay_deal = {}
    local cost_card = get_aa_cost()

    for position, uid in pairs(seated_position_uid_map) do
        if need_pay(uid) then
            table_insert(deduct_pay_deal, { f = reduce_roomcards, id = uid, params = { uid, cost_card } })
        end
    end

    local ok, co_results = cocall(COCALL_TIMEOUT, deduct_pay_deal)
    if not ok then
        errlog(string_format("pay_fee failed to cocall co_results(%s)", tostring_r(co_results)))
        return false
    end

    dbglog(string_format("co_results(%s)", tostring_r(co_results)))

    local pay_ok_map = {}
    local ok = get_pay_result_detail(co_results, pay_ok_map)
    if not ok then
        return_roomcards(co_results, cost_card)
        return false
    end

    for uid, ret in pairs(pay_ok_map) do
        had_paid_fee_uid_map[uid] = true
    end

    notify_all_roomcard_change(pay_ok_map)
    return true
end

local function change_player_status_to_playing()
    for position, uid in pairs(seated_position_uid_map) do
        if uid then
            local player_info = player_info_list[uid]

            player_info.status = PLAYER_STATUS_PLAYING
            curr_kpqz_instance:init_player_data(uid, player_info.position, player_info.coins)
        end
    end
end

local function start_kpqz_instance()
    local cost = self_game_config.cost
    local dizhu = self_game_config.dizhu

    local instance = kpqz:new()

    instance:init(self_game_config.max_seat_num, card_type_times_map, sorted_can_bet_times_list,
        cost, dizhu, self_game_type, sorted_can_push_times_list)
    curr_kpqz_instance = instance
end

local function meet_start_condition()
    --1.判断座位上的玩家数量是否超过2个
    if not player_num_meet_condition() then
        return false
    end

    --1.判断每个玩家的台费是否够
    local ok = pay_fee_success()
    if not ok then
        return false
    end

    start_kpqz_instance()
    change_player_status_to_playing()
    return true
end

local function time_to_start(curr_time)
    if curr_time > game_start_wait_start_time + GAME_TIME_WAIT_START then
        return true
    end

    return false
end

local function time_to_bet(curr_time)
    if curr_time > game_start_start_time + GAME_TIME_START then
        return true
    end

    return false
end

local function get_player_can_push(action_uid, banker_uid)
    local player_info = player_info_list[action_uid]
    if player_info.last_had_done_push then
        dbglog(string_format("uid(%d) last_had_done_push", action_uid))
        return false
    end

    local round1_action_record_map = two_round_action_record_map[1]
    local round2_action_record_map = two_round_action_record_map[2]

    if not round1_action_record_map then
        return false
    end

    local round1_record = round1_action_record_map[action_uid]    
    if not round1_record then
        return false
    end

    if not round2_action_record_map then
        if action_uid ~= banker_uid and round1_record.win then
            player_info.last_had_done_push = true
            return true
        else
            return false
        end
    end

    local round2_record = round2_action_record_map[action_uid]

    if round2_record then
        if action_uid ~= banker_uid and not round2_record.win and round1_record.win then
            player_info.last_had_done_push = true
            return true
        else
            return false
        end
    else
        if action_uid ~= banker_uid and round1_record.win then
            player_info.last_had_done_push = true
            return true
        else
            return false
        end
    end
end

local function assembly_ntf_grab_banker_result(uid, same_max_times_list)
    local banker_info = curr_kpqz_instance:get_banker_info()
    local msg = {}
    local can_push = get_player_can_push(uid, banker_info.banker_uid)

    msg.banker_uid = banker_info.banker_uid
    msg.left_time = GAME_TIME_BET
    msg.banker_times = banker_info.banker_times
    msg.same_max_times_list = same_max_times_list
    msg.can_push = can_push

    return msg
end

local function send_ntf_grab_banker_result_msg(same_max_times_list)
	local player_data_map = curr_kpqz_instance:get_player_data_map()
	for uid, player_data in pairs(player_data_map) do
        local msg = assembly_ntf_grab_banker_result(uid, same_max_times_list)
        local game_session = uid_game_session_map[uid]
        send_to_gateway(uid, game_session, "kpqz.NTF_GRAB_BANKER_RESULT", msg)
    end

    for _, uid in pairs(noseat_players_list) do 
       local msg = assembly_ntf_grab_banker_result(uid, same_max_times_list)
       local game_session = uid_game_session_map[uid]
       send_to_gateway(uid, game_session, "kpqz.NTF_GRAB_BANKER_RESULT", msg)
    end
end

local function time_to_cacl_cattle(curr_time)
    if curr_time > game_start_bet_time + GAME_TIME_BET then
        return true
    end

    return false
end

local function get_fifth_card(side_cards_map, position)
    local cards = side_cards_map[position]
    return cards[constant.NN_MAX_CARDS_COUNT]
end

local function send_fifth_card_msg()
    local player_data_map = curr_kpqz_instance:get_player_data_map()
    local side_cards_map = curr_kpqz_instance:get_side_cards_map()

    for uid, player_data in pairs(player_data_map) do
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_CACL_CATTLE
        msg.fifth_card = get_fifth_card(side_cards_map, player_data.side)

        send_to_gateway(uid, game_session, "kpqz.NTF_FIFTH_CARD", msg)
    end

    for _, uid in pairs(noseat_players_list) do 
        local msg = {}
        local game_session = uid_game_session_map[uid]

        msg.left_time = GAME_TIME_CACL_CATTLE
        msg.fifth_card = {}
        send_to_gateway(uid, game_session, "kpqz.NTF_FIFTH_CARD", msg)
    end
end

local function send_timeout_ntf_cacl_cattle_msg()
    local player_data_map = curr_kpqz_instance:get_player_data_map()

    for uid, player_data in pairs(player_data_map) do
        if not player_data.calc_end then
            notify_all("kpqz.NTF_CACL_CATTLE", { uid = uid })
            player_data.calc_end = true
        end
    end
end

local function time_to_show_and_settle(curr_time)
    if curr_time > game_start_calc_cattle_time + GAME_TIME_CACL_CATTLE then
        return true
    end

    return false
end

local function time_to_wait_start(curr_time)
    if curr_time > game_start_show_and_settle_time + GAME_TIME_SHOW_AND_SETTLE then
        return true
    end

    return false
end

local function reset_game_data()
    curr_kpqz_instance = nil
    game_bet_result_list = {}
end

local function deal_bet_logic(uid, bet_times, action_type)
    local ok, ret = curr_kpqz_instance:bet_action(uid, bet_times, action_type)
    if not ok then
        errlog(string_format("uid(%d) failed to bet reason(%d)", uid, ret))
        return false, ret
    end

    notify_all("kpqz.NTF_BET", { uid = uid, bet_times = bet_times })
    return true
end

local function set_room_owner_confirm_status(status)
    room_owner_confirm_status = status
end

local function room_owner_confirm_mode()
    return self_game_config.is_creator_start
end

local function room_owner_confirm()
    return room_owner_confirm_status == true
end

local function update(curr_time)
    --dbglog(string_format("----------%s-------------", TABLE_STATUS_STRING[curr_status]))

    if curr_status == GAME_STATUS_INIT then
        if not status_change_flag then
            status_change_flag = true
        end

        if status_change_flag then
            if room_owner_confirm_mode() then
                curr_status = GAME_STATUS_ROOM_OWNER_CONFIRM
            elseif player_num_meet_condition() then
                curr_status = GAME_STATUS_WAIT_START
            end

            status_change_flag = false
        end
    elseif curr_status == GAME_STATUS_ROOM_OWNER_CONFIRM then
        if room_owner_confirm() and player_num_meet_condition() then
            curr_status = GAME_STATUS_WAIT_START
        end
    elseif curr_status == GAME_STATUS_WAIT_START then
        if not status_change_flag then
            if player_num_meet_condition() then
                game_start_wait_start_time = curr_time  
                notify_all("kpqz.NTF_WAIT_START", { left_time = GAME_TIME_WAIT_START })                
                status_change_flag = true       
            else
                curr_status = GAME_STATUS_INIT
            end
        end

        if status_change_flag and time_to_start(curr_time) then
            if meet_start_condition() then                
            	curr_round = curr_round + 1
                send_ntf_start_msg()
                curr_status = GAME_STATUS_START
                status_change_flag = false
            else
                curr_status = GAME_STATUS_INIT
                status_change_flag = false
                --notify_all("kpqz.NTF_INIT", {})
            end
        end
    elseif curr_status == GAME_STATUS_START then
        if not status_change_flag then   
            game_start_start_time = curr_time              

            if curr_round == 1 then 
                R().fmatchsvr(1):send(".table_mgr", "ftable_start", self_game_config.password)
            end
            
            curr_kpqz_instance:start_grab_banker()
            status_change_flag = true
        end

        if status_change_flag and (curr_kpqz_instance:grab_banker_complete() or time_to_bet(curr_time)) then
            local banker_info, same_max_times_list = curr_kpqz_instance:determine_banker()
            send_ntf_grab_banker_result_msg(same_max_times_list)
            curr_kpqz_instance:start_bet()
            curr_status = GAME_STATUS_BET
            status_change_flag = false
        end
    elseif curr_status == GAME_STATUS_BET then
        if not status_change_flag then            
            game_start_bet_time = curr_time
            status_change_flag = true
        end

        if status_change_flag and (curr_kpqz_instance:bet_complete() or time_to_cacl_cattle(curr_time)) then
            curr_kpqz_instance:do_default_bet()
            send_fifth_card_msg()
            curr_kpqz_instance:start_cacl_cattle()
            curr_status = GAME_STATUS_CACL_CATTLE
            status_change_flag = false
        end
    elseif curr_status == GAME_STATUS_CACL_CATTLE then
        if not status_change_flag then
            game_start_calc_cattle_time = curr_time           
            status_change_flag = true
        end

        local settle_flag = status_change_flag and (curr_kpqz_instance:calc_cattle_complete() or time_to_show_and_settle(curr_time))
        if settle_flag then 
            send_timeout_ntf_cacl_cattle_msg()
            curr_kpqz_instance:judge_result()

            local ok, ret = xpcall(audit_game_result, debug.traceback, curr_kpqz_instance:get_game_result())
            if not ok then
                errlog(ret)
            end
            curr_status = GAME_STATUS_SHOW_AND_SETTLE
            status_change_flag = false
        end
    elseif curr_status == GAME_STATUS_SHOW_AND_SETTLE then
        if not status_change_flag then
            game_start_show_and_settle_time = curr_time        
            status_change_flag = true                        
        end

        if curr_round >= self_game_config.total_round then
            is_round_over = true
        end

        if status_change_flag and time_to_wait_start(curr_time) then
            clear_offline_players()
            when_not_action_standup()        
            curr_status = GAME_STATUS_WAIT_START 
            reset_game_data()
            status_change_flag = false        
        end
    else
        errlog(string_format("curr_status(%d) error", curr_status))
    end
    
    check_close_table(curr_time)
end

local function game_update()
    curr_status = GAME_STATUS_INIT

    while true do
        local curr_time = util.get_now_time()
        local ok, ret = xpcall(update, debug.traceback, curr_time)
        if not ok then 
            errlog(ret)
        end
        skynet.sleep(50)
    end
end

function internal.get_player_num()
    return get_seated_num()
end

function internal.get_game_type()
    return self_game_type, self_table_type
end

local function get_non_robot_players_num()
    local player_num = 0

    for _, uid in pairs(seated_position_uid_map) do
        if uid then
            if not system_do_banker(uid) then
                player_num = player_num + 1
            end      
        end  
    end

    return player_num    
end

function internal.get_user_playing_info()
    local playing_info = {}
    playing_info.game_type = self_game_type
    playing_info.table_type = self_table_type
    playing_info.total = get_non_robot_players_num()

    return playing_info
end

local function get_all_bet_list()
    local all_bet_list = {}

    if not curr_kpqz_instance then
        return all_bet_list
    end

    local all_bet_list = curr_kpqz_instance:get_all_bet_list()
    return all_bet_list
end

local function get_bet_result_list()
    local bet_result_list = {}

    if not curr_kpqz_instance then
        return bet_result_list
    end

    if curr_status ~= GAME_STATUS_SHOW_AND_SETTLE then
        return bet_result_list
    end

    return game_bet_result_list
end

local function get_all_five_cards(side_cards_map, position)
    local self_card_list = side_cards_map[position]
    return self_card_list
end

local function get_self_card_list(uid)
    local self_card_list = {}

    if not curr_kpqz_instance then
        dbglog(string_format("-----------1------uid(%d)-------------------------", uid))
        return self_card_list
    end

    if not curr_kpqz_instance:check_player_playing(uid) then
        dbglog(string_format("-----------2------uid(%d)-------------------------", uid))
        return self_card_list
    end

    local side_cards_map = curr_kpqz_instance:get_side_cards_map()
    local player_data_map = curr_kpqz_instance:get_player_data_map()
    local player_data = player_data_map[uid]

    if curr_status == GAME_STATUS_START or curr_status == GAME_STATUS_BET then
      self_card_list = get_first_four_cards(side_cards_map, player_data.side)
    elseif curr_status == GAME_STATUS_CACL_CATTLE or curr_status == GAME_STATUS_SHOW_AND_SETTLE then
        self_card_list = get_all_five_cards(side_cards_map, player_data.side)
    end

    dbglog(string_format("========================>uid(%d) self_card_list(%s)", uid, tostring_r(self_card_list)))

    return self_card_list
end

local function get_final_banker_bet_info()
    local bet_info = {}

    if not curr_kpqz_instance then
        return bet_info
    end

    local banker_info = curr_kpqz_instance:get_banker_info()
    return { uid = banker_info.banker_uid, banker_times = banker_info.banker_times }
end

local function get_left_time_and_game_status()
    local lefe_time = 0
    local next_status_time = 0
    local curr_time = util.get_now_time()
    local client_status = GAME_STATUS_INIT

    if curr_status == GAME_STATUS_INIT or curr_status == GAME_STATUS_ROOM_OWNER_CONFIRM then
        lefe_time = 0
        client_status = GAME_STATUS_INIT
    elseif curr_status == GAME_STATUS_WAIT_START then
        next_status_time = game_start_wait_start_time + GAME_TIME_WAIT_START
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS_WAIT_START
    elseif curr_status == GAME_STATUS_START then
        next_status_time = game_start_start_time + GAME_TIME_START
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS_START
    elseif curr_status == GAME_STATUS_BET then
        next_status_time = game_start_bet_time + GAME_TIME_BET
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS_BET
    elseif curr_status == GAME_STATUS_CACL_CATTLE then
        next_status_time = game_start_calc_cattle_time + GAME_TIME_CACL_CATTLE
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS_CACL_CATTLE
    elseif curr_status == GAME_STATUS_SHOW_AND_SETTLE then
        next_status_time = game_start_show_and_settle_time + GAME_TIME_SHOW_AND_SETTLE
        lefe_time = next_status_time - curr_time
        client_status = GAME_STATUS_SHOW_AND_SETTLE
    else
        errlog(string_format("curr_status(%d) error", curr_status))
    end

    if lefe_time < 0 then
        lefe_time = 0
    end
    
    dbglog(string_format("curr_status(%d)(%s) left_time(%d)", curr_status, TABLE_STATUS_STRING[curr_status], lefe_time))
    return lefe_time, client_status
end

local function get_grab_banker_list()
    local grab_banker_list = {}

    if not curr_kpqz_instance then
        return grab_banker_list
    end

    local player_data_map = curr_kpqz_instance:get_player_data_map()

    for uid, player_data in pairs(player_data_map) do
        local bet_info = { uid = player_data.uid, banker_times = player_data.banker_times }
        table_insert(grab_banker_list, bet_info)
    end

    return grab_banker_list
end

local function get_calc_cattle_end_list()
    local end_list = {}

    if not curr_kpqz_instance then
        return end_list
    end

    local player_data_map = curr_kpqz_instance:get_player_data_map()

    for uid, player_data in pairs(player_data_map) do
        if player_data.calc_end then
            table_insert(end_list, uid)
        end
    end

    return end_list
end

local function get_game_status_info(uid)
    local game_status_info = {}
    local lefe_time, client_status = get_left_time_and_game_status()

    game_status_info.left_time = lefe_time
    game_status_info.curr_status = client_status
    game_status_info.sitdown_coins = self_game_config.min
    game_status_info.all_bet_list = get_all_bet_list()
    game_status_info.grab_banker_list = get_grab_banker_list()
    game_status_info.bet_result_list = get_bet_result_list()
    game_status_info.self_card_list = get_self_card_list(uid)
    game_status_info.calc_cattle_end_list = get_calc_cattle_end_list()       
    game_status_info.banker_bet = get_final_banker_bet_info()
    game_status_info.banker_max_times = get_grab_banker_max_times(uid)
    game_status_info.curr_round = curr_round
    
    return game_status_info
end

function get_game_detail(uid)
    local game_detail = {}

    game_detail.result = error_code.RSP_SUCESS
    game_detail.player_info_list = get_rsp_seat_player_info_list()
    game_detail.game_status = get_game_status_info(uid)

    return game_detail
end

--资源加载完成后请求动态数据
function handler.REQ_GAME_DETAIL(uid, msg, game_session)
    dbglog(string_format("uid(%d) game_session(%d) REQ_GAME_DETAIL", uid, game_session))    

    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_GAME_DETAIL", { result = ret })
        return true
    end

    local game_detail = get_game_detail(uid)

    send_to_gateway(uid, game_session, "kpqz.RSP_GAME_DETAIL", game_detail)
    return true
end

local function check_grab_banker_req_param(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if not curr_kpqz_instance then
        errlog("curr_kpqz_instance is nil uid:", uid)
        return false, error_code.GRAB_BANKER_DEADLINE_ERROR
    end

    return true
end

function handler.REQ_GRAB_BANKER(uid, msg, game_session)
    local banker_times = msg.banker_times
    dbglog(string_format("uid(%d) banker_times(%d) is_robot(%s) REQ_GRAB_BANKER", uid, banker_times, tostring(is_robot(uid))))

    local ok, ret = check_grab_banker_req_param(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_GRAB_BANKER", { result = ret })        
        return true
    end 

    local ok, ret = curr_kpqz_instance:grab_banker_action(uid, banker_times, kpqz.PLAYER_ACTION)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_GRAB_BANKER", { result = ret })        
        return true
    end 

    send_to_gateway(uid, game_session, "kpqz.RSP_GRAB_BANKER", { result = error_code.RSP_SUCESS })
    notify_all("kpqz.NTF_GRAB_BANKER", { uid = uid, banker_times = banker_times })    
    return true
end

local function check_cacl_cattle_req_param(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    if not curr_kpqz_instance then
        errlog("curr_kpqz_instance is nil uid:", uid)
        return false, error_code.SHOW_CARD_DEADLINE_ERROR
    end

    return true
end

function handler.REQ_CACL_CATTLE(uid, msg, game_session)
    dbglog(string_format("uid(%d) is_robot(%s) REQ_CACL_CATTLE", uid, tostring(is_robot(uid))))

    local ok, ret = check_cacl_cattle_req_param(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_CACL_CATTLE", { result = ret })        
        return true
    end

    local ok, ret = curr_kpqz_instance:cacl_cattle_action(uid, kpqz.PLAYER_ACTION)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_CACL_CATTLE", { result = ret })        
        return true
    end

    send_to_gateway(uid, game_session, "kpqz.RSP_CACL_CATTLE", { result = error_code.RSP_SUCESS })
    notify_all("kpqz.NTF_CACL_CATTLE", { uid = uid })
    return true
end

local function check_room_owner_confirm_start_req(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    if not game_status_correct(GAME_STATUS_ROOM_OWNER_CONFIRM) then
        return false, error_code.NOT_ROOM_OWNER_CONFIRM_TIME_ERROR
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    local player_info = player_info_list[uid]

    if uid ~= self_game_config.creator_uid then
        errlog(string_format("uid(%d) creator_uid(%d)", uid, self_game_config.creator_uid))
        return false, error_code.PERMISSION_DENIDE
    end

    if not player_num_meet_condition() then
        errlog(string_format("uid(%d) NO_ENOUGH_PLAYERS_ROOM_OWNER_CONFIRM_TART", uid))
        return false, error_code.NO_ENOUGH_PLAYERS_ROOM_OWNER_CONFIRM_TART
    end

    return true
end

function handler.REQ_ROOM_OWNER_CONFIRM_START(uid, msg, game_session)
    dbglog(string_format("uid(%d) game_session(%d)", uid, game_session))

    local ok, ret = check_room_owner_confirm_start_req(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_ROOM_OWNER_CONFIRM_START",{ result = ret })    
        return true        
    end

    send_to_gateway(uid, game_session, "kpqz.RSP_ROOM_OWNER_CONFIRM_START",{ result = error_code.RSP_SUCESS })    
    set_room_owner_confirm_status(true)
    return true
end

local function check_dismiss_req_param(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if uid ~= self_game_config.creator_uid then
        errlog(string_format("uid(%d) creator_uid(%d)", uid, self_game_config.creator_uid))
        return false, error_code.PERMISSION_DENIDE
    end    

    local ok, ret = xpcall(dissmiss_table, debug.traceback, DISS_REASON_CREATOR)
    if not ok then
        errlog(ret)
        return false
    end

    return true
end

function handler.REQ_DISMISS(uid, msg, game_session)
    dbglog(string_format("uid(%d) REQ_DISMISS", uid))

    local ok, ret = check_dismiss_req_param(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_DISMISS", { result = ret })
        return true
    end

    local uid_list = {}

    for uid, _ in pairs(player_info_list) do
        table_insert(uid_list, uid)
    end    

    send_to_gateway(uid, game_session, "kpqz.RSP_DISMISS", { result = error_code.RSP_SUCESS })
    skynet.send(".table_mgr", "lua", "on_table_delete", self_table_id, uid_list)
    skynet.exit()
    return true
end

local function check_bet_req_params(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if not curr_kpqz_instance then
        errlog("curr_kpqz_instance is nil uid:", uid)
        return false, error_code.BET_DEADLINE_ERROR
    end

    return true
end

function handler.REQ_BET(uid, msg, game_session)
    local bet_times = msg.bet_times
    dbglog(string_format("uid(%d) bet_times(%d) is_robot(%s) REQ_BET", uid, bet_times, tostring(is_robot(uid))))

    local ok, ret = check_bet_req_params(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_BET", { result = ret })
        return true
    end
    
    local ok, ret = deal_bet_logic(uid, bet_times, kpqz.PLAYER_ACTION)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_BET", { result = ret })
        return true
    end

    send_to_gateway(uid, game_session, "kpqz.RSP_BET", { result = error_code.RSP_SUCESS })
    return true
end

local function check_authorized_seat_param(uid, position, creator_uid)
    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if not self_game_config.is_authorized_seat then
        errlog(string_format("uid(%d) no need authorized", uid))
        return false, error_code.AUTHORIZED_SEAT_FAIL_CAN_NOT_AUTHORIZED
    end

    if creator_uid == uid then
        errlog(string_format("uid(%d) self is the creator_uid", uid))
        return false, error_code.AUTHORIZED_SEAT_FAIL_NOT_CREATOR
    end

    if creator_uid ~= self_game_config.creator_uid then
        errlog(string_format("uid(%d) creator_uid(%d) self_game_config.creator_uid(%d)", uid, creator_uid, self_game_config.creator_uid))
        return false, error_code.INPUT_ERROR
    end

    if position < 1 or position > self_game_config.max_seat_num then
        errlog(string_format("uid(%d) position(%d) max_seat_num(%d)", uid, position, self_game_config.max_seat_num))
        return false, error_code.INPUT_ERROR
    end

    if seated_position_uid_map[position] then
        errlog(string_format("uid(%d) position(%d) SEAT_IS_EMPLOY", uid, position))
        return false, error_code.SEAT_IS_EMPLOY
    end

    local player_info = player_info_list[uid]

    if player_info.position then
        errlog(string_format("uid(%d) position(%d) had seated", uid, player_info.position))
        return false, error_code.SEAT_FAIL_HAVE_SEAT
    end

    return true
end

local function can_pay_aa_for_authorized_seat(uid)    
    local cost_card = get_aa_cost()
    local ok, able = R().basesvr({ key = uid }):call(".msg_handler", "can_reduce_roomcards", uid, cost_card)
    if not ok then
        errlog(string_format("uid(%d) call basesvr can_reduce_roomcards error", uid))
        return false, error_code.RSP_FAIL
    end

    if not able then
        errlog(string_format("uid(%s) call basesvr can_reduce_roomcards able is nil", uid))
        return false, error_code.ROOMCARD_IS_NOT_ENOUGH
    end

    return true
end

local function notify_room_owner_logic(uid)
    local authorized_time = uid_authorized_time_map[uid]
    if not authorized_time then
        uid_authorized_time_map[uid] = util.get_now_time()
    else
        return false, error_code.AUTHORIZED_SEAT_FAIL_REPEATED
    end

    local player_info = player_info_list[uid]

    local notify = {
        apply_uid = uid,
        passwd = self_game_config.password,
        table_gid = self_table_gid,
        position = player_info.position,
        game_type = self_game_type,
        apply_name = player_info.name
    }

    local ok, ret = R().hallsvr({ key = self_game_config.creator_uid }):call(".msg_handler", "send_notify", 
        { self_game_config.creator_uid }, constant.GENERAL_NOTICE_TYPE.AUTHORIZED_SEAT_MSG, notify, true)

    if not ok or ret ~= 0 then
        errlog("call hallsvr send_notify error", uid, ok, ret)
        return false, error_code.CLUB_SEND_NOTIFY_FAIL
    end

    return true
end

function handler.REQ_AUTHORIZED_SEAT(uid, msg, game_session)
    local creator_uid = msg.creator_uid
    local position = msg.position

    dbglog(string_format("uid(%d) creator_uid(%d) position(%d)", uid, creator_uid, position))

    if not game_session_correct(uid, game_session) then
        return false
    end    

    local ok, ret = check_authorized_seat_param(uid, position, creator_uid)
    if not ok then
        send_to_gateway(uid, game_session, "table.RSP_AUTHORIZED_SEAT", { result = ret })
        return true
    end

    if pass_authorized_uid_map[uid] then
        deal_sit_down(uid, position)
        update_player_status(uid, PALYER_STATUS_READY)
        --send_to_gateway(uid, game_session, "table.RSP_AUTHORIZED_SEAT", { result = error_code.RSP_SUCESS })
        send_to_gateway(uid, game_session, "table.RSP_AUTHORIZED_SEAT", { result = error_code.ALREADY_AUTHORIZED })
        return true
    end

    if self_game_config.is_aa then
        local ok, ret = can_pay_aa_for_authorized_seat(uid)
        if not ok then
            send_to_gateway(uid, game_session, "table.RSP_AUTHORIZED_SEAT", { result = ret })
            return true
        end
    end    

    local ok, ret = notify_room_owner_logic(uid)
    if not ok then
        send_to_gateway(uid, game_session, "table.RSP_AUTHORIZED_SEAT", { result = ret })    
        return true
    end
    
    send_to_gateway(uid, game_session, "table.RSP_AUTHORIZED_SEAT", { result = error_code.RSP_SUCESS })
    return true
end

local function check_agree_authorized_seat_param(uid, apply_uid, passwd)
    if not uid_authorized_time_map[apply_uid] then
        errlog(string_format("uid(%d) not authorized AGREE_AUTHORIZED_SEAT_FAIL_NOT_APPLY", uid))
        return false, error_code.AGREE_AUTHORIZED_SEAT_FAIL_NOT_APPLY
    end

    uid_authorized_time_map[apply_uid] = nil

    if uid ~= self_game_config.creator_uid then
        errlog(string_format("uid(%d) creator_uid(%d) AGREE_AUTHORIZED_SEAT_FAIL_NOT_CREATOR", uid, self_game_config.creator_uid))
        return false, error_code.AGREE_AUTHORIZED_SEAT_FAIL_NOT_CREATOR
    end

    if passwd ~= self_game_config.password then
        errlog("uid(%d) passwd(%d) self_game_config.password(%d)", uid, passwd, self_game_config.password)
        return false, error_code.AGREE_AUTHORIZED_SEAT_FAIL_PASSWORD_ERROR
    end

    local ok, ret = check_player_info_exist(apply_uid)
    if not ok then
        return false, error_code.NO_PLAYER_IN_TABLE
    end

    local player_info = player_info_list[apply_uid]
    if player_info.position then
        errlog(string_format("apply_uid(%d) position(%d) had seat", apply_uid, player_info.position))
        return false, error_code.SEAT_FAIL_HAVE_SEAT
    end

    if pass_authorized_uid_map[apply_uid] then
        errlog(string_format("apply_uid(%d) had authorized", apply_uid))
        return error_code.AGREE_AUTHORIZED_SEAT_FAIL_HAVE_AUTHORIZED
    end

    return true
end

function internal.agree_authorized_seat(uid, name, msg)        
    local apply_uid = msg.apply_uid
    local passwd = msg.passwd
    local is_agree = msg.is_agree

    dbglog(string_format("uid(%d) apply_uid(%d) passwd(%d)", uid, apply_uid, passwd))    

    local ok, ret = check_agree_authorized_seat_param(uid, apply_uid, passwd)
    if not ok then
        return ret
    end
    
    if not is_agree then
        send_to_gateway(apply_uid, uid_game_session_map[apply_uid], "room.NTF_AGREE_AUTHORIZED_SEAT",
            { creator_id = uid, creator_name = name, is_agree = false })
        return error_code.RSP_SUCESS
    end

    if self_game_config.is_aa and not had_paid_fee_uid_map[apply_uid] then
        local ok, ret = can_pay_aa_for_authorized_seat(uid)
        if not ok then
            return ret
        end
    end

    local player_info = player_info_list[apply_uid]

    local ok, ret = deal_auto_sit_down(player_info)
    if not ok then
        return ret
    end

    pass_authorized_uid_map[apply_uid] = true
    return error_code.RSP_SUCESS
end

local function check_sitdown_req_param(uid, game_session, position)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end   

    if seated_position_uid_map[position] ~= false then
        errlog(string_format("uid(%d) position(%d) SEAT_IS_EMPLOY", uid, position))
        return false, error_code.SEAT_IS_EMPLOY
    end

    local player_info = player_info_list[uid]

    if player_info.position then
        errlog(string_format("uid(%d) SEAT_FAIL_HAVE_SEAT", uid))
        return false, error_code.SEAT_FAIL_HAVE_SEAT
    end

    return true
end

function handler.REQ_SITDOWN(uid, msg, game_session)
    local position = msg.position
    dbglog(string_format("uid(%d) position(%d) is_robot(%s) REQ_SITDOWN", uid, position, tostring(is_robot(uid))))

    local ok, ret = check_sitdown_req_param(uid, game_session, position)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_SITDOWN", { result = ret })
        return true
    end

    deal_sit_down(uid, position)
    send_to_gateway(uid, game_session, "kpqz.RSP_SITDOWN", { result = error_code.RSP_SUCESS })
    return true
end

local function player_is_playing_game(uid)
    if not curr_kpqz_instance then
        return false
    end

    return curr_kpqz_instance:check_player_playing(uid)
end

local function check_standup_req_param(uid, game_session)
    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        return false, ret
    end

    if player_is_playing_game(uid) then
        errlog(string_format("uid(%d) during game CANT_STANDUP_DURING_PLAYING_ERROR", uid))
        return false, error_code.CANT_STANDUP_DURING_PLAYING_ERROR
    end

    local player_info = player_info_list[uid]
    local position = player_info.position

    if not position then
        errlog(string_format("uid(%d) not in position UP_FAIL_NOT_HAVE_SEAT", uid))
        return false, error_code.UP_FAIL_NOT_HAVE_SEAT
    end

    if seated_position_uid_map[position] == false then
        errlog(string_format("uid(%d) not in position(%d) UP_FAIL_NOT_HAVE_SEAT", uid, position))
        return false, error_code.UP_FAIL_NOT_HAVE_SEAT
    end

    if seated_position_uid_map[position] ~= uid then
        errlog(string_format("uid(%d) not in position(%d) UP_FAIL_NOT_HAVE_SEAT", uid, position))
        return false, error_code.UP_FAIL_NOT_HAVE_SEAT
    end

    return true
end

function handler.REQ_STANDUP(uid, msg, game_session)
    dbglog(string_format("uid(%d) is_robot(%s) REQ_STANDUP", uid, tostring(is_robot(uid))))

    local ok, ret = check_standup_req_param(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_STANDUP", { result = ret })
        return true    
    end

    deal_stand_up(uid, 0)
    send_to_gateway(uid, game_session, "kpqz.RSP_STANDUP", { result = error_code.RSP_SUCESS })
    return true
end

function handler.REQ_CHAT(uid, msg)
    dbglog(string_format("uid(%d) REQ_CHAT", uid))

    local player_info = player_info_list[uid]
    local curr_time = util.get_now_time()

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, uid_game_session_map[uid], "table.RSP_CHAT", { result = ret })
        return true
    end

    if player_info.last_chat_time and curr_time - player_info.last_chat_time < 1 then
        send_to_gateway(uid, uid_game_session_map[uid], "table.RSP_CHAT", { result = error_code.REQ_CHAT_TOO_FAST })
        return true
    end

    player_info.last_chat_time = curr_time
    send_to_gateway(uid, uid_game_session_map[uid], "table.RSP_CHAT", { result = error_code.RSP_SUCESS })

    local ntf = { 
        uid = uid, 
        type = msg.type, 
        picture_id = msg.picture_id, 
        content = msg.content, 
        voice = msg.voice, 
        content_id = msg.content_id, 
        name = player_info.name, 
        icon = player_info.icon, 
        sex = player_info.sex, 
     }

    notify_all("table.NTF_CHAT", ntf)
    return true
end

local function notify_matchsvr_table_stats(is_delete)
    local table_stats = {}
    if is_delete then
        table_stats.table_gid = self_table_gid
        table_stats.table_type = self_table_type
        R().newmatchsvr(1):send(".table_mgr", "delete_table_stats", table_stats)
    else
        table_stats.table_type = self_table_type
        --table_stats.left_num = table_def.game_player_num[self_game_type] - get_seated_num()
        table_stats.left_num = self_game_config.max_seat_num - get_seated_num()  
        table_stats.table_gid = self_table_gid
        R().newmatchsvr(1):send(".table_mgr", "update_table_stats", table_stats)
        dbglog(string_format("table_stats(%s)", tostring_r(table_stats)))
    end
end

local function deal_player_regular_leave(uid)
    if is_robot(uid) then
        deal_robot_leave(uid)
        return
    end

    local game_session = uid_game_session_map[uid]
    clear_and_unlock_offline_player(uid)

    notify_matchsvr_table_stats()
    skynet.send(".table_mgr", "lua", "leave", uid)   
end

function handler.REQ_LEAVE(uid, msg, game_session)
    dbglog(string_format("uid(%d) is_robot(%s) REQ_LEAVE", uid, tostring(is_robot(uid))))

    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, game_session, "kpqz.RSP_LEAVE", { result = ret })
        return true
    end


    if player_is_playing_game(uid) and  curr_status == GAME_STATUS_WAIT_START then
        errlog(string_format("uid(%d) during game CANT_STANDUP_DURING_PLAYING_ERROR", uid))
		send_to_gateway(uid, game_session, "kpqz.RSP_LEAVE", { result = error_code.RSP_SUCESS })
        uid_game_session_map[uid] = OFFLINE_CLIENT_FD
        return true
    end
   
    
    --if (is_round_over == false and curr_round > 0) then
    if (is_round_over == false and curr_round > 0 and not player_is_playing_game(uid)) then
    	send_to_gateway(uid, game_session, "kpqz.RSP_LEAVE", { result = error_code.RSP_SUCESS })
    	uid_game_session_map[uid] = OFFLINE_CLIENT_FD
    	return true
    end

    deal_player_regular_leave(uid)    
    send_to_gateway(uid, game_session, "kpqz.RSP_LEAVE", { result = error_code.RSP_SUCESS })
    watch_session(game_session, uid, false)
    return true
end

function handler.REQ_USE_MAGIC_PICTURE(uid, msg, game_session)
    local dst_id = msg.dst_id
    local picture_id = msg.picture_id
    local reason_code = 0 

    dbglog(string_format("uid(%d) dst_id(%d) picture_id(%d) REQ_USE_MAGIC_PICTURE", uid, dst_id, picture_id))

    if not game_session_correct(uid, game_session) then
        return false
    end

    local ok, ret = check_player_info_exist(uid)
    if not ok then
        send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", { result = ret })
        return true
    end

    local dst_player_info = player_info_list[dst_id]
    if not dst_player_info.position then
        errlog(string_format("uid(%d) dst_id(%d) dst player no seat", uid, dst_id))
        send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", { result = error_code.TABLE_USE_MAGIC_PICTURE_MUST_SITDOWN })
        return true
    end

    local ret = reduce_coins(uid, constant.USE_MAGIC_PICTURE_COIN, reason.USE_MAGIC_PICTRUE, get_extra_bill_info(uid))
    if not ret then
        send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", { result = error_code.GOLD_IS_NOT_ENOUGH })
        return true
    end

    send_to_gateway(uid, game_session, "table.RSP_USE_MAGIC_PICTURE", {})

    notify_all("table.NTF_USE_MAGIC_PICTURE", { 
        src_id = uid, 
        dst_id = dst_id, 
        picture_id = picture_id, 
        coins = ret.curr, 
     })

    utils.notify_money_changed(uid, { coins = ret.curr }, game_session)
    return true
end

function internal.disconnect(uid, game_session)
    dbglog(string_format("uid(%d) game_session(%d) disconnect", uid, game_session))

    if not game_session_correct(uid, game_session) then
        return false
    end

    if player_is_playing_game(uid) then
		errlog(string_format("disconnect uid(%d) during game CANT_STANDUP_DURING_PLAYING_ERROR", uid))
        uid_game_session_map[uid] = OFFLINE_CLIENT_FD
        return true
    end
--[[
    if curr_kpqz_instance then
        if is_banker(uid) then
            curr_banker_info.banker_uid = constant.SYSTEM_DO_BANKER_UID
            curr_kpqz_instance:set_banker(curr_banker_info.banker_uid)
        end
    end

    deal_player_regular_leave(uid)
--]]
    deal_player_regular_leave(uid)
    watch_session(game_session, uid, false)	
    return true
end

function internal.close_server()
    closing_server = true
end

function internal.enter(uid, game_session)
    dbglog(string_format("uid(%d) game_session(%d) enter", uid, game_session))

    local ok, result = deal_enter(uid, game_session)
    if not ok then
        send_to_gateway(uid, game_session, "table.RSP_ENTER", { result = result })
        return false
    end

    local msg = { result = error_code.RSP_SUCESS, game_type = self_game_type, table_type = self_table_type, kpqz_enter_info = get_enter_info(),name = self_game_config.name, }

    send_to_gateway(uid, game_session, "table.RSP_ENTER", msg)
    watch_session(game_session, uid, true)
    R().friendsvr(1):send(".info_mgr", "update_info", uid, { curr_game_type = self_game_type, curr_table_type = self_table_type })
    return true
end

function internal.touch(key)
    if recycling_table_start_time then
        recycling_table_start_time = recycling_table_start_time + 10
    end
end

local function get_card_type_times_map()
    local config_special_type = constant.NN_NAME_SPECIAL_CARD_TYPE_MAP
    local special_card_type = self_game_config.special_type
    local times_option = self_game_config.times_option
    local card_type_times_map = {}


    for _, card_type in pairs(special_card_type) do
        if card_type == config_special_type.SHUNZI then
            card_type_times_map[card_type] = 5
        elseif card_type == config_special_type.FLUSH_CATTLE then
            card_type_times_map[card_type] = 5
        elseif card_type == config_special_type.GOURD_CATTLE then
            card_type_times_map[card_type] = 5
        elseif card_type == config_special_type.FOUR_BOMBS then
            card_type_times_map[card_type] = 6
        elseif card_type == config_special_type.FIVE_COLOR_CATTLES then
            card_type_times_map[card_type] = 6
        elseif card_type == config_special_type.FIVE_CALVES then
            card_type_times_map[card_type] = 8
        elseif card_type == config_special_type.STRAIGHT_FLUSH then
            card_type_times_map[card_type] = 10
        end
    end

    if times_option == 1 then
        card_type_times_map[10] = 3
        card_type_times_map[9] = 2
        card_type_times_map[8] = 2
        card_type_times_map[7] = 2

        for card_type = 0, 6 do
            card_type_times_map[card_type] = 1
        end
    elseif times_option == 2 then
        card_type_times_map[10] = 4
        card_type_times_map[9] = 3
        card_type_times_map[8] = 2
        card_type_times_map[7] = 2

        for card_type = 0, 6 do
            card_type_times_map[card_type] = 1
        end
    elseif times_option == 3 then
        card_type_times_map[10] = 3
        card_type_times_map[9] = 2
        card_type_times_map[8] = 2

        for card_type = 0, 7 do
            card_type_times_map[card_type] = 1
        end
    end

    return card_type_times_map
end

local function sort_can_bet_times_list()
    sorted_can_bet_times_list = constant.ORDINARY_ROOM_NN_CAN_BET_TIMES_LIST
    table_sort(sorted_can_bet_times_list)

    sorted_can_push_times_list = constant.ORDINARY_ROOM_NN_CAN_PUSH_TIMES_LIST
    table_sort(sorted_can_push_times_list)

    dbglog(string_format("----------sorted_can_bet_times_list(%s)", tostring_r(sorted_can_bet_times_list)))
end

function internal.start(conf)  
    self_table_id = conf.table_id
    self_table_type = conf.table_type
    self_table_gid = conf.table_gid
    self_game_type = conf.game_type

    self_game_config = conf.payload
    
    
    --for_test
    --self_game_config.total_round = 1
    
    
    curr_round = self_game_config.curr_round

    dbglog(string_format("self_game_config(%s)", tostring_r(self_game_config)))

    if self_game_config.is_creator_start then
        self_game_config.least_num = 2
    end 

    for i = 1, self_game_config.max_seat_num do
        table_insert(seated_position_uid_map, false)
    end

    sort_can_bet_times_list()
    game_start_wait_start_time = util.get_now_time()
    card_type_times_map = get_card_type_times_map()
    
    
    set_table_status_string()

    dbglog(string_format("conf(%s) \nid<%d>, type(%d), gid(%d)", tostring_r(conf), self_table_id, self_table_type, self_table_gid))
    skynet.fork(game_update)
    return true
end

function internal.exit()
    skynet.exit()
end

function internal.update_coins_on_table(uid, coins,safe_box)
    dbglog(string_format("uid(%d) coins(%d) update coins", uid, coins))

    local player_info = player_info_list[uid]
    if player_info then
        player_info.coins = coins
        if player_info.position then
            notify_all("table.NTF_MONEY_CHANGED", { chged_list = { { uid = uid, coins = coins } } })
        end
        if safe_box then
            player_info.safe_box = safe_box
        end
    end
    return true
end

function internal.get_ftable_info()
    local table_data = {}
    table_data.table_type = self_table_type
    table_data.left_round = self_game_config.total_round - curr_round
    table_data.is_aa = self_game_config.is_aa
    table_data.cur_player_num = get_seated_num()
    table_data.max_player_num = self_game_config.max_seat_num
    table_data.curr_round = curr_round
    dbglog(string_format("-----------table_data(%s)---------", tostring_r(table_data)))
    return table_data
end

local function update_player_heartbeat(uid)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.last_ping_time = util.get_now_time()
    end
end

local function get_msg_module_name(msgid)
    local m = msgdef.id_to_name[msgid]

    if not m then 
        return false, false
    end

    return m[1], m[2] --[1]->module, [2]->name
end

local function dispatch_client_message(game_session, uid, msg, size)
    local _, msgid, pbmsg, pbsize = proxypack.unpack_client_message(msg, size)
    local module, name = get_msg_module_name(msgid)

    if not module or not name then
        dbglog(string_format("msgid(%d) module(%s) name(%s) invalid msgid", msgid, module, name))
        return false
    end

    local pbname = module .. "." .. name
    local req_msg = pb.decode(pbname, pbmsg, pbsize)

    dbglog(string_format("address(%s) uid(%s) pbname(%s) content(%s)", 
        skynet.address(skynet.self()), tostring_r(uid), pbname, tostring_r(req_msg)))

    local f = handler[name]
    if not f then
        errlog(string_format("pbname(%s) unknown action", pbname))
        return false
    end

    local ret = f(uid, req_msg, game_session)
    if not ret then
        errlog(string_format("failed to handle requrest(%s.%s)", module, name))
        return false
    end

    update_player_heartbeat(uid)
    return true
end

local function dispatch(_, _, game_session, uid, data)
    local msg, size = proxypack.pack_raw(data)
    local ok, errmsg = xpcall(dispatch_client_message, debug.traceback, game_session, uid, msg, size)
    skynet.trash(msg, size)  --这里需要保证内存被释放
    if not ok then 
        errlog(errmsg)
    end
end

skynet.register_protocol { 
    name = "client", 
    id = skynet.PTYPE_CLIENT, 
    unpack = skynet.unpack, 
    dispatch = dispatch
 }

skynet.start(function()
    skynet.dispatch("lua", function(session, source, action, ...)
        dbglog("internal request", action, ...)
        local f = internal[action]
        if f then
            skynet.retpack(f(...))
        else
            handler[action](...)
        end
    end)

    sharedata.query("global_configs")
    global_configs = setmetatable({}, { 
        __index = function(t, k) 
            return sharedata.query("global_configs")[k]
        end
     })
end)
end
